﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Configuration;
using DataLayer.dbml;
using DataLayer.Utilities;

namespace DataLayer.Controller
{
    /// <summary>
    /// Clase que controla las operaciones relacioandas con el log de transacciones
    /// </summary>
    public class LogTransactionController : GenericController<LOG_TRANSACCION, DataClassesDataContext>
    {
        /// <summary>
        /// Enuemracion de los posibles estados d euna transacción
        /// </summary>
        public enum Estado
        { 
            Fallida,
            Exito
        }

        private const string TIPO_TRANSACCION_OPORTUNIDAD = "CREACION OPORTUNIDAD";
        private const string TIPO_TRANSACCION_CIERRE_OT = "CIERRE-OT";
        private const string TIPO_TRANSACCION_CREACION_CASO = "CREACION CASO";
        private const string TIPO_TRANSACCION_CREACION_COMENTARIO = "CREACION COMENTARIO";

        /// <summary>
        /// Retorna los registros de los envios fallidos que deben ser reenviados
        /// </summary>
        /// <returns></returns>
        private List<LOG_TRANSACCION> GetElementosFallidos()
        {
            var q = from l in DataContext.LOG_TRANSACCION
                    where l.ESTADO == "Fallida" && l.ENVIOS < 3
                    select l;

            return q.ToList<LOG_TRANSACCION>();
        }

        /// <summary>
        /// Actualiza el registro correspondiente a un intento de envío
        /// </summary>
        /// <param name="obj">Objeto de tipo LOG_transaccion, con la información de la transacción enviada</param>
        /// <param name="estado">Estado obtenido del intento de envío</param>
        /// <param name="observacion">Observaciones de la transacción</param>
        private void ActualizarEnvio(LOG_TRANSACCION obj, Estado estado, string observacion)
        {
            string strEstado = estado.ToString();

            //Se obtiene el objeto
            LOG_TRANSACCION logActual = (from l in DataContext.LOG_TRANSACCION
                             where l.ID_LOG == obj.ID_LOG
                             select l).SingleOrDefault();

            logActual.ENVIOS = logActual.ENVIOS + 1;
            logActual.ESTADO = strEstado;
            logActual.OBSERVACION = observacion;
            DataContext.SubmitChanges();   
         
            //Si ya se llegó a 3 intentos y es aún fallido se envía correo
            if ((logActual.ENVIOS == 3) && (logActual.ESTADO == "Fallida"))
                this.EnviarCorreo(logActual);

        }

        /// <summary>
        /// Envia correo informando que no se pudo ejcutar exitosamente un ws y ya se reintento 3 veces
        /// </summary>
        /// <param name="obj"></param>
        private void EnviarCorreo(LOG_TRANSACCION obj)
        {
            string mensaje = "Se completaron 3 reintentos fallidos de llamado a un web service de SalesForce: <br/>";
            mensaje += "ID_LOG: " + obj.ID_LOG.ToString() + "<br/>";
            mensaje += "TIPO_TRANSACCION: " + obj.TIPO_TRANSACCION + "<br/>";
            mensaje += "TIPO_TRANSACCION: " + obj.TIPO_TRANSACCION + "<br/>";
            mensaje += "OBSERVACION: " + obj.OBSERVACION + "<br/>";
            mensaje += "ELEMENTO_ENVIADO: " + obj.ELEMENTO_ENVIADO;
            Utilidades.sendMail(ConfigurationManager.AppSettings["correoDestino"].ToString(), ConfigurationManager.AppSettings["userNameMail"].ToString(), "Reenvio de WS fallido", "", "Portal Axesat", mensaje);
        }

        /// <summary>
        /// Crea una oportunidad
        /// </summary>
        /// <param name="OPParameter"></param>
        /// <param name="OPResult"></param>
        private void CrearOportunidad(DataLayer.SForceOPService.parametrosOportunidad OPParameter, DataLayer.SForceOPService.creaOportunidadRespuesta_cls OPResult)
        {
            //Se crea la oportunidad en las tablas del portal
            OPORTUNIDAD opo = new OPORTUNIDAD();
            opo.ID_OPORTUNIDADSDFC = OPResult.strIdOportunidadSDFC;
            opo.ID_OPORTUNIDAD = OPResult.strCodigoOportunidad;
            opo.NOMBRE_OPORTUNIDAD = OPParameter.strNombreOportunidad;
            opo.ETAPA = "Contacto inicial 10%";
            opo.ID_PROYECTOSFDC = OPResult.strIdProyectoSFC;
            opo.ANCHO_BANDA = OPParameter.strAnchoBanda;

            OpportunityController.Insert(opo);

            //Se crea el cliente si es nuevo
            if (OPParameter.strNuevoClienteFinal.Length == 0)
            {
                string cuentaSF = "";
                DataTable dtt = FinalClientController.GetById(OPParameter.strIdClienteFinalSFDC);
                if (dtt.Rows.Count > 0)
                {
                    cuentaSF = dtt.Rows[0]["idCuenta"].ToString();
                }        

                CLIENTE_FINAL clienteFinal = new CLIENTE_FINAL();
                clienteFinal.ID_CLIENTEFINALSDFC = OPResult.strIdClienteFinal;
                clienteFinal.NIT_CLIENTEFINAL = OPResult.strNitCienteFinal;
                clienteFinal.NOMBRE_CLIENTEFINAL = OPResult.strNombreClienteFinal.ToUpper();
                FinalClientController.Insert(clienteFinal);

                CUENTA_CLIENTEFINAL cuenta = new CUENTA_CLIENTEFINAL();
                cuenta.ID_CLIENTEFINALSDFC = OPResult.strIdClienteFinal;
                cuenta.ID_CUENTASFDC = cuentaSF;
                AccountFinalClientController.Insert(cuenta);
            }

        }

        /// <summary>
        /// Actualiza una OT
        /// </summary>
        /// <param name="OTParameter"></param>
        private void ActualizarOT(DataLayer.SForceOTService.ActualizarOrdenDeTrabajoParametros OTParameter)
        {
            OTController.CloseOT(OTParameter.nombreDeOrdenesDeTrabajo, OTParameter.strComentario);
        }

        /// <summary>
        /// Crea un caso
        /// </summary>
        /// <param name="param"></param>
        /// <param name="resultado"></param>
        private void CrearCaso(DataLayer.SForceCase.parametrosCreacionCaso param, DataLayer.SForceCase.creacionCasoRespuesta_cls resultado)
        {
            SERVICIO objServicio = new SERVICIO();
            objServicio = ServicesController.GetServiceById(param.strIdServicio);

            //Se crea el caso en las tablas del portal
            CASO caso = new CASO();
            caso.ID_CASOSDFC = resultado.strIdCasoSFDC;
            caso.ID_CASO = resultado.strCodigoCaso;
            caso.TIPO_CASO = param.strNameRecordType;
            caso.MOTIVO_CASO = param.strReason;
            caso.PROBLEMA_ENCONTRADO = string.Empty;
            caso.ESTADO_CASO = "Nuevo";
            caso.FECHA_HORA_APERTURA = resultado.dateFechaTransaccion;
            caso.DESCRIPCION = param.strDescription;
            caso.ID_CUENTASFDC = objServicio.ID_CUENTASFDC;
            caso.ID_PROYECTOSFDC = objServicio.ID_PROYECTOSFDC;
            caso.ID_OPORTUNIDADSDFC = objServicio.ID_OPORTUNIDADSDFC;
            caso.ID_CUENTASDFC_ID_CLIENTEFINALSDFC = objServicio.ID_CUENTASDFC_ID_CLIENTEFINALSDFC;
            caso.ID_SERVICIOSDFC = objServicio.ID_SERVICIOSDFC;
            CaseController.Insert(caso);
        }

        /// <summary>
        /// Crea un comentario de caso
        /// </summary>
        /// <param name="param"></param>
        /// <param name="respuesta"></param>
        private void CrearComentarioCaso(DataLayer.SForceCaseComment.parametrosComentarioCaso param, DataLayer.SForceCaseComment.creacionComentarioRespuesta_cls respuesta)
        {
            //Se crea el comentario en las tablas del portal
            COMENTARIOS_CASO comentario = new COMENTARIOS_CASO();
            comentario.ID_COMENTARIO = respuesta.strIdComentarioSFDC;
            comentario.DESCRIPCION = param.strDescripcion;
            comentario.FECHA_COMENTARIO = respuesta.dateFechaTransaccion;
            comentario.TIPO_COMENTARIO = "Comentarios Caso";
            comentario.ID_CASOSDFC = param.strIdCasoSFDC;
            comentario.PERSONA_REALIZA_COMENTARIO = "";
            comentario.TIPO_LLAMADA = "Llamada Saliente";
            CaseCommentController.Insert(comentario);
        }

        /// <summary>
        /// Intenta el reenvio de las transacciones que han fallado. Cuando se alcanza cierto numero de intentos
        /// se envía correo informando
        /// </summary>
        /// <returns>Mensaje informativo de qué se hizo</returns>
        public string Reenviar()
        {
            StringBuilder retorno = new StringBuilder();

            //Se obtienen las transacciones que se deben reintentar
            List<LOG_TRANSACCION> lista = this.GetElementosFallidos();

            //Se recorre la lista y se reintenta el envío            
            object obj;
            foreach (LOG_TRANSACCION logT in lista)
            {
                if (string.IsNullOrEmpty(logT.ELEMENTO_ENVIADO))
                    continue;

                try
                {
                    switch (logT.TIPO_TRANSACCION)
                    {
                        case TIPO_TRANSACCION_OPORTUNIDAD:
                            DataLayer.SForceOPService.parametrosOportunidad OPParameter = new DataLayer.SForceOPService.parametrosOportunidad();
                            OPParameter = (DataLayer.SForceOPService.parametrosOportunidad)Utilidades.Deserializar(logT.ELEMENTO_ENVIADO, OPParameter.GetType());
                            DataLayer.SForceOPService.creaOportunidadRespuesta_cls OPResult = SFController.CrearOportunidad(OPParameter);
                            if (OPResult.strEstadoTransaccion.Equals("Fallida"))
                            {
                                //Se actualiza la info de la transaccion                                
                                this.ActualizarEnvio(logT, Estado.Fallida, OPResult.strObservaciones);
                                retorno.Append("ID_LOG " + logT.ID_LOG.ToString() + ": Reintento fallido");
                            }
                            else
                            {
                                //Se crea la oportunidad en las tablas del portal
                                this.ActualizarEnvio(logT, Estado.Exito, OPResult.strObservaciones);
                                this.CrearOportunidad(OPParameter, OPResult);
                                retorno.Append("ID_LOG " + logT.ID_LOG.ToString() + ": Reintento exitoso");
                            }

                            break;
                        case TIPO_TRANSACCION_CIERRE_OT:
                            DataLayer.SForceOTService.ActualizarOrdenDeTrabajoParametros OTParameter = new DataLayer.SForceOTService.ActualizarOrdenDeTrabajoParametros();
                            OTParameter = (DataLayer.SForceOTService.ActualizarOrdenDeTrabajoParametros)Utilidades.Deserializar(logT.ELEMENTO_ENVIADO, OTParameter.GetType());
                            DataLayer.SForceOTService.ActualizarOrdenDeTrabajoRespuesta result = SFController.ActualizarOT(OTParameter);
                            if (result.strEstadoTransaccion.Equals("Fallida"))
                            {
                                //Se actualiza la info de la transaccion                                
                                this.ActualizarEnvio(logT, Estado.Fallida, result.strObservaciones);
                                retorno.Append("ID_LOG " + logT.ID_LOG.ToString() + ": Reintento fallido");
                            }
                            else
                            {
                                this.ActualizarEnvio(logT, Estado.Exito, result.strObservaciones);
                                this.ActualizarOT(OTParameter);
                                retorno.Append("ID_LOG " + logT.ID_LOG.ToString() + ": Reintento exitoso");
                            }

                            break;
                        case TIPO_TRANSACCION_CREACION_CASO:
                            DataLayer.SForceCase.parametrosCreacionCaso param = new DataLayer.SForceCase.parametrosCreacionCaso();
                            param = (DataLayer.SForceCase.parametrosCreacionCaso)Utilidades.Deserializar(logT.ELEMENTO_ENVIADO, param.GetType());
                            DataLayer.SForceCase.creacionCasoRespuesta_cls resultado = SFController.CrearCaso(param);
                            if (resultado.strEstadoTransaccion.Equals("Fallida"))
                            {
                                //Se actualiza la info de la transaccion                                
                                this.ActualizarEnvio(logT, Estado.Fallida, resultado.strObservaciones);
                                retorno.Append("ID_LOG " + logT.ID_LOG.ToString() + ": Reintento fallido");
                            }
                            else
                            {
                                this.ActualizarEnvio(logT, Estado.Exito, resultado.strObservaciones);
                                this.CrearCaso(param, resultado);
                                retorno.Append("ID_LOG " + logT.ID_LOG.ToString() + ": Reintento exitoso");
                            }

                            break;
                        case TIPO_TRANSACCION_CREACION_COMENTARIO:
                            DataLayer.SForceCaseComment.parametrosComentarioCaso paramCom = new DataLayer.SForceCaseComment.parametrosComentarioCaso();
                            paramCom = (DataLayer.SForceCaseComment.parametrosComentarioCaso)Utilidades.Deserializar(logT.ELEMENTO_ENVIADO, paramCom.GetType());
                            DataLayer.SForceCaseComment.creacionComentarioRespuesta_cls respuesta = SFController.CrearComentarioCaso(paramCom);
                            if (respuesta.strEstadoTransaccion.Equals("Fallida"))
                            {
                                //Se actualiza la info de la transaccion                                
                                this.ActualizarEnvio(logT, Estado.Fallida, respuesta.strObservaciones);
                                retorno.Append("ID_LOG " + logT.ID_LOG.ToString() + ": Reintento fallido");
                            }
                            else
                            {
                                this.ActualizarEnvio(logT, Estado.Exito, respuesta.strObservaciones);
                                this.CrearComentarioCaso(paramCom, respuesta);
                                retorno.Append("ID_LOG " + logT.ID_LOG.ToString() + ": Reintento exitoso");
                            }
                            break;
                    }

                }
                catch (Exception ex)
                {
                    string inner = string.Empty;
                    if (ex.InnerException != null)
                        inner = ex.InnerException.ToString();

                    retorno.Append("ID_LOG " + logT.ID_LOG.ToString() + ":. ERROR: " + ex.Message + " INNEREXCEPCTION: " + inner);                    
                }
            }

            return retorno.ToString();
        }


    }
}
